home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / plnk081.zip / pilot-link.0.8.1 / parsedate.y < prev    next >
Text File  |  1997-05-23  |  21KB  |  813 lines

  1. %{
  2. /* $Revision: 1.12 $
  3. **
  4. **  Originally written by Steven M. Bellovin <smb@research.att.com> while
  5. **  at the University of North Carolina at Chapel Hill.  Later tweaked by
  6. **  a couple of people on Usenet.  Completely overhauled by Rich $alz
  7. **  <rsalz@osf.org> and Jim Berets <jberets@bbn.com> in August, 1990.
  8. **  Further revised (removed obsolete constructs and cleaned up timezone
  9. **  names) in August, 1991, by Rich.  Paul Eggert <eggert@twinsun.com>
  10. **  helped in September, 1992.
  11. **
  12. **  This grammar has six shift/reduce conflicts.
  13. **
  14. **  This code is in the public domain and has no copyright.
  15. */
  16. /* SUPPRESS 530 *//* Empty body for statement */
  17. /* SUPPRESS 593 on yyerrlab *//* Label was not used */
  18. /* SUPPRESS 593 on yynewstate *//* Label was not used */
  19. /* SUPPRESS 595 on yypvt *//* Automatic variable may be used before set */
  20. #include <stdio.h>
  21. #include <sys/types.h>
  22. #include <ctype.h>
  23. #include <time.h>
  24.  
  25. int date_lex();
  26.  
  27. #define yyparse        date_parse
  28. #define yylex        date_lex
  29. #define yyerror        date_error
  30.  
  31.  
  32.     /* See the LeapYears table in Convert. */
  33. #define EPOCH        1970
  34. #define END_OF_TIME    2038
  35.     /* Constants for general time calculations. */
  36. #define DST_OFFSET    1
  37. #define SECSPERDAY    (24L * 60L * 60L)
  38.     /* Readability for TABLE stuff. */
  39. #define HOUR(x)        (x * 60)
  40.  
  41. #define LPAREN        '('
  42. #define RPAREN        ')'
  43. #define IS7BIT(x)    ((unsigned int)(x) < 0200)
  44.  
  45. #define SIZEOF(array)    ((int)(sizeof array / sizeof array[0]))
  46. #define ENDOF(array)    (&array[SIZEOF(array)])
  47.  
  48.  
  49. /*
  50. **  An entry in the lexical lookup table.
  51. */
  52. typedef struct _TABLE {
  53.     char    *name;
  54.     int        type;
  55.     time_t    value;
  56. } TABLE;
  57.  
  58. /*
  59. **  Daylight-savings mode:  on, off, or not yet known.
  60. */
  61. typedef enum _DSTMODE {
  62.     DSTon, DSToff, DSTmaybe
  63. } DSTMODE;
  64.  
  65. /*
  66. **  Meridian:  am, pm, or 24-hour style.
  67. */
  68. typedef enum _MERIDIAN {
  69.     MERam, MERpm, MER24
  70. } MERIDIAN;
  71.  
  72.  
  73. /*
  74. **  Global variables.  We could get rid of most of them by using a yacc
  75. **  union, but this is more efficient.  (This routine predates the
  76. **  yacc %union construct.)
  77. */
  78. static char    *yyInput;
  79. static DSTMODE    yyDSTmode;
  80. static int    yyHaveDate;
  81. static int    yyHaveRel;
  82. static int    yyHaveTime;
  83. static time_t    yyTimezone;
  84. static time_t    yyDay;
  85. static time_t    yyHour;
  86. static time_t    yyMinutes;
  87. static time_t    yyMonth;
  88. static time_t    yySeconds;
  89. static time_t    yyYear;
  90. static MERIDIAN    yyMeridian;
  91. static time_t    yyRelMonth;
  92. static time_t    yyRelSeconds;
  93.  
  94.  
  95. extern struct tm    *localtime();
  96.  
  97. static void        date_error();
  98. %}
  99.  
  100. %union {
  101.     time_t        Number;
  102.     enum _MERIDIAN    Meridian;
  103. }
  104.  
  105. %token    tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
  106. %token    tUNUMBER tZONE
  107.  
  108. %type    <Number>    tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
  109. %type    <Number>    tSNUMBER tUNUMBER tZONE numzone zone
  110. %type    <Meridian>    tMERIDIAN o_merid
  111.  
  112. %%
  113.  
  114. spec    : /* NULL */
  115.     | spec item
  116.     ;
  117.  
  118. item    : time {
  119.         yyHaveTime++;
  120. #ifdef lint
  121.         /* I am compulsive about lint natterings... */
  122.         if (yyHaveTime == -1) {
  123.         YYERROR;
  124.         }
  125. #endif /* lint */
  126.     }
  127.     | time zone {
  128.         yyHaveTime++;
  129.         yyTimezone = $2;
  130.     }
  131.     | date {
  132.         yyHaveDate++;
  133.     }
  134.     | rel {
  135.         yyHaveRel = 1;
  136.     }
  137.     ;
  138.  
  139. time    : tUNUMBER o_merid {
  140.         if ($1 < 100) {
  141.         yyHour = $1;
  142.         yyMinutes = 0;
  143.         }
  144.         else {
  145.         yyHour = $1 / 100;
  146.         yyMinutes = $1 % 100;
  147.         }
  148.         yySeconds = 0;
  149.         yyMeridian = $2;
  150.     }
  151.     | tUNUMBER ':' tUNUMBER o_merid {
  152.         yyHour = $1;
  153.         yyMinutes = $3;
  154.         yySeconds = 0;
  155.         yyMeridian = $4;
  156.     }
  157.     | tUNUMBER ':' tUNUMBER numzone {
  158.         yyHour = $1;
  159.         yyMinutes = $3;
  160.         yyTimezone = $4;
  161.         yyMeridian = MER24;
  162.         yyDSTmode = DSToff;
  163.     }
  164.     | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
  165.         yyHour = $1;
  166.         yyMinutes = $3;
  167.         yySeconds = $5;
  168.         yyMeridian = $6;
  169.     }
  170.     | tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
  171.         yyHour = $1;
  172.         yyMinutes = $3;
  173.         yySeconds = $5;
  174.         yyTimezone = $6;
  175.         yyMeridian = MER24;
  176.         yyDSTmode = DSToff;
  177.     }
  178.     ;
  179.  
  180. zone    : tZONE {
  181.         $$ = $1;
  182.         yyDSTmode = DSToff;
  183.     }
  184.     | tDAYZONE {
  185.         $$ = $1;
  186.         yyDSTmode = DSTon;
  187.     }
  188.     | tZONE numzone {
  189.         /* Only allow "GMT+300" and "GMT-0800" */
  190.         if ($1 != 0) {
  191.         YYABORT;
  192.         }
  193.         $$ = $2;
  194.         yyDSTmode = DSToff;
  195.     }
  196.     | numzone {
  197.         $$ = $1;
  198.         yyDSTmode = DSToff;
  199.     }
  200.     ;
  201.  
  202. numzone    : tSNUMBER {
  203.         int        i;
  204.  
  205.         /* Unix and GMT and numeric timezones -- a little confusing. */
  206.         if ($1 < 0) {
  207.         /* Don't work with negative modulus. */
  208.         $1 = -$1;
  209.         if ($1 > 9999 || (i = $1 % 100) >= 60) {
  210.             YYABORT;
  211.         }
  212.         $$ = ($1 / 100) * 60 + i;
  213.         }
  214.         else {
  215.         if ($1 > 9999 || (i = $1 % 100) >= 60) {
  216.             YYABORT;
  217.         }
  218.         $$ = -(($1 / 100) * 60 + i);
  219.         }
  220.     }
  221.     ;
  222.  
  223. date    : tUNUMBER '/' tUNUMBER {
  224.         yyMonth = $1;
  225.         yyDay = $3;
  226.     }
  227.     | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
  228.         if ($1 > 100) {
  229.         yyYear = $1;
  230.         yyMonth = $3;
  231.         yyDay = $5;
  232.         }
  233.         else {
  234.         yyMonth = $1;
  235.         yyDay = $3;
  236.         yyYear = $5;
  237.         }
  238.     }
  239.     | tMONTH tUNUMBER {
  240.         yyMonth = $1;
  241.         yyDay = $2;
  242.     }
  243.     | tMONTH tUNUMBER ',' tUNUMBER {
  244.         yyMonth = $1;
  245.         yyDay = $2;
  246.         yyYear = $4;
  247.     }
  248.     | tUNUMBER tMONTH {
  249.         yyDay = $1;
  250.         yyMonth = $2;
  251.     }
  252.     | tUNUMBER tMONTH tUNUMBER {
  253.         yyDay = $1;
  254.         yyMonth = $2;
  255.         yyYear = $3;
  256.     }
  257.     | tDAY ',' tUNUMBER tMONTH tUNUMBER {
  258.         yyDay = $3;
  259.         yyMonth = $4;
  260.         yyYear = $5;
  261.     }
  262.     ;
  263.  
  264. rel    : tSNUMBER tSEC_UNIT {
  265.         yyRelSeconds += $1 * $2;
  266.     }
  267.     | tUNUMBER tSEC_UNIT {
  268.         yyRelSeconds += $1 * $2;
  269.     }
  270.     | tSNUMBER tMONTH_UNIT {
  271.         yyRelMonth += $1 * $2;
  272.     }
  273.     | tUNUMBER tMONTH_UNIT {
  274.         yyRelMonth += $1 * $2;
  275.     }
  276.     ;
  277.  
  278. o_merid    : /* NULL */ {
  279.         $$ = MER24;
  280.     }
  281.     | tMERIDIAN {
  282.         $$ = $1;
  283.     }
  284.     ;
  285.  
  286. %%
  287.  
  288. /* Month and day table. */
  289. static TABLE    MonthDayTable[] = {
  290.     { "january",    tMONTH,  1 },
  291.     { "february",    tMONTH,  2 },
  292.     { "march",        tMONTH,  3 },
  293.     { "april",        tMONTH,  4 },
  294.     { "may",        tMONTH,  5 },
  295.     { "june",        tMONTH,  6 },
  296.     { "july",        tMONTH,  7 },
  297.     { "august",        tMONTH,  8 },
  298.     { "september",    tMONTH,  9 },
  299.     { "october",    tMONTH, 10 },
  300.     { "november",    tMONTH, 11 },
  301.     { "december",    tMONTH, 12 },
  302.     /* The value of the day isn't used... */
  303.     { "sunday",        tDAY, 0 },
  304.     { "monday",        tDAY, 0 },
  305.     { "tuesday",    tDAY, 0 },
  306.     { "wednesday",    tDAY, 0 },
  307.     { "thursday",    tDAY, 0 },
  308.     { "friday",        tDAY, 0 },
  309.     { "saturday",    tDAY, 0 },
  310. };
  311.  
  312. /* Time units table. */
  313. static TABLE    UnitsTable[] = {
  314.     { "year",        tMONTH_UNIT,    12 },
  315.     { "month",        tMONTH_UNIT,    1 },
  316.     { "week",        tSEC_UNIT,    7L * 24 * 60 * 60 },
  317.     { "day",        tSEC_UNIT,    1L * 24 * 60 * 60 },
  318.     { "hour",        tSEC_UNIT,    60 * 60 },
  319.     { "minute",        tSEC_UNIT,    60 },
  320.     { "min",        tSEC_UNIT,    60 },
  321.     { "second",        tSEC_UNIT,    1 },
  322.     { "sec",        tSEC_UNIT,    1 },
  323. };
  324.  
  325. /* Timezone table. */
  326. static TABLE    TimezoneTable[] = {
  327.     { "gmt",    tZONE,     HOUR( 0) },    /* Greenwich Mean */
  328.     { "ut",    tZONE,     HOUR( 0) },    /* Universal */
  329.     { "utc",    tZONE,     HOUR( 0) },    /* Universal Coordinated */
  330.     { "cut",    tZONE,     HOUR( 0) },    /* Coordinated Universal */
  331.     { "z",    tZONE,     HOUR( 0) },    /* Greenwich Mean */
  332.     { "wet",    tZONE,     HOUR( 0) },    /* Western European */
  333.     { "bst",    tDAYZONE,  HOUR( 0) },    /* British Summer */
  334.     { "nst",    tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
  335.     { "ndt",    tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
  336.     { "ast",    tZONE,     HOUR( 4) },    /* Atlantic Standard */
  337.     { "adt",    tDAYZONE,  HOUR( 4) },    /* Atlantic Daylight */
  338.     { "est",    tZONE,     HOUR( 5) },    /* Eastern Standard */
  339.     { "edt",    tDAYZONE,  HOUR( 5) },    /* Eastern Daylight */
  340.     { "cst",    tZONE,     HOUR( 6) },    /* Central Standard */
  341.     { "cdt",    tDAYZONE,  HOUR( 6) },    /* Central Daylight */
  342.     { "mst",    tZONE,     HOUR( 7) },    /* Mountain Standard */
  343.     { "mdt",    tDAYZONE,  HOUR( 7) },    /* Mountain Daylight */
  344.     { "pst",    tZONE,     HOUR( 8) },    /* Pacific Standard */
  345.     { "pdt",    tDAYZONE,  HOUR( 8) },    /* Pacific Daylight */
  346.     { "yst",    tZONE,     HOUR( 9) },    /* Yukon Standard */
  347.     { "ydt",    tDAYZONE,  HOUR( 9) },    /* Yukon Daylight */
  348.     { "akst",    tZONE,     HOUR( 9) },    /* Alaska Standard */
  349.     { "akdt",    tDAYZONE,  HOUR( 9) },    /* Alaska Daylight */
  350.     { "hst",    tZONE,     HOUR(10) },    /* Hawaii Standard */
  351.     { "hast",    tZONE,     HOUR(10) },    /* Hawaii-Aleutian Standard */
  352.     { "hadt",    tDAYZONE,  HOUR(10) },    /* Hawaii-Aleutian Daylight */
  353.     { "ces",    tDAYZONE,  -HOUR(1) },    /* Central European Summer */
  354.     { "cest",    tDAYZONE,  -HOUR(1) },    /* Central European Summer */
  355.     { "mez",    tZONE,     -HOUR(1) },    /* Middle European */
  356.     { "mezt",    tDAYZONE,  -HOUR(1) },    /* Middle European Summer */
  357.     { "cet",    tZONE,     -HOUR(1) },    /* Central European */
  358.     { "met",    tZONE,     -HOUR(1) },    /* Middle European */
  359.     { "eet",    tZONE,     -HOUR(2) },    /* Eastern Europe */
  360.     { "msk",    tZONE,     -HOUR(3) },    /* Moscow Winter */
  361.     { "msd",    tDAYZONE,  -HOUR(3) },    /* Moscow Summer */
  362.     { "wast",    tZONE,     -HOUR(8) },    /* West Australian Standard */
  363.     { "wadt",    tDAYZONE,  -HOUR(8) },    /* West Australian Daylight */
  364.     { "hkt",    tZONE,     -HOUR(8) },    /* Hong Kong */
  365.     { "cct",    tZONE,     -HOUR(8) },    /* China Coast */
  366.     { "jst",    tZONE,     -HOUR(9) },    /* Japan Standard */
  367.     { "kst",    tZONE,     -HOUR(9) },    /* Korean Standard */
  368.     { "kdt",    tZONE,     -HOUR(9) },    /* Korean Daylight */
  369.     { "cast",    tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
  370.     { "cadt",    tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
  371.     { "east",    tZONE,     -HOUR(10) },    /* Eastern Australian Standard */
  372.     { "eadt",    tDAYZONE,  -HOUR(10) },    /* Eastern Australian Daylight */
  373.     { "nzst",    tZONE,     -HOUR(12) },    /* New Zealand Standard */
  374.     { "nzdt",    tDAYZONE,  -HOUR(12) },    /* New Zealand Daylight */
  375.  
  376.     /* For completeness we include the following entries. */
  377. #if 0
  378.  
  379.     /* Duplicate names.  Either they conflict with a zone listed above
  380.      * (which is either more likely to be seen or just been in circulation
  381.      * longer), or they conflict with another zone in this section and
  382.      * we could not reasonably choose one over the other. */
  383.     { "fst",    tZONE,     HOUR( 2) },    /* Fernando De Noronha Standard */
  384.     { "fdt",    tDAYZONE,  HOUR( 2) },    /* Fernando De Noronha Daylight */
  385.     { "bst",    tZONE,     HOUR( 3) },    /* Brazil Standard */
  386.     { "est",    tZONE,     HOUR( 3) },    /* Eastern Standard (Brazil) */
  387.     { "edt",    tDAYZONE,  HOUR( 3) },    /* Eastern Daylight (Brazil) */
  388.     { "wst",    tZONE,     HOUR( 4) },    /* Western Standard (Brazil) */
  389.     { "wdt",    tDAYZONE,  HOUR( 4) },    /* Western Daylight (Brazil) */
  390.     { "cst",    tZONE,     HOUR( 5) },    /* Chile Standard */
  391.     { "cdt",    tDAYZONE,  HOUR( 5) },    /* Chile Daylight */
  392.     { "ast",    tZONE,     HOUR( 5) },    /* Acre Standard */
  393.     { "adt",    tDAYZONE,  HOUR( 5) },    /* Acre Daylight */
  394.     { "cst",    tZONE,     HOUR( 5) },    /* Cuba Standard */
  395.     { "cdt",    tDAYZONE,  HOUR( 5) },    /* Cuba Daylight */
  396.     { "est",    tZONE,     HOUR( 6) },    /* Easter Island Standard */
  397.     { "edt",    tDAYZONE,  HOUR( 6) },    /* Easter Island Daylight */
  398.     { "sst",    tZONE,     HOUR(11) },    /* Samoa Standard */
  399.     { "ist",    tZONE,     -HOUR(2) },    /* Israel Standard */
  400.     { "idt",    tDAYZONE,  -HOUR(2) },    /* Israel Daylight */
  401.     { "idt",    tDAYZONE,  -(HOUR(3)+30) }, /* Iran Daylight */
  402.     { "ist",    tZONE,     -(HOUR(3)+30) }, /* Iran Standard */
  403.     { "cst",     tZONE,     -HOUR(8) },    /* China Standard */
  404.     { "cdt",     tDAYZONE,  -HOUR(8) },    /* China Daylight */
  405.     { "sst",     tZONE,     -HOUR(8) },    /* Singapore Standard */
  406.  
  407.     /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
  408.     { "gst",    tZONE,     HOUR( 3) },    /* Greenland Standard */
  409.     { "wat",    tZONE,     -HOUR(1) },    /* West Africa */
  410.     { "at",    tZONE,     HOUR( 2) },    /* Azores */
  411.     { "gst",    tZONE,     -HOUR(10) },    /* Guam Standard */
  412.     { "nft",    tZONE,     HOUR(3)+30 }, /* Newfoundland */
  413.     { "idlw",    tZONE,     HOUR(12) },    /* International Date Line West */
  414.     { "mewt",    tZONE,     -HOUR(1) },    /* Middle European Winter */
  415.     { "mest",    tDAYZONE,  -HOUR(1) },    /* Middle European Summer */
  416.     { "swt",    tZONE,     -HOUR(1) },    /* Swedish Winter */
  417.     { "sst",    tDAYZONE,  -HOUR(1) },    /* Swedish Summer */
  418.     { "fwt",    tZONE,     -HOUR(1) },    /* French Winter */
  419.     { "fst",    tDAYZONE,  -HOUR(1) },    /* French Summer */
  420.     { "bt",    tZONE,     -HOUR(3) },    /* Baghdad */
  421.     { "it",    tZONE,     -(HOUR(3)+30) }, /* Iran */
  422.     { "zp4",    tZONE,     -HOUR(4) },    /* USSR Zone 3 */
  423.     { "zp5",    tZONE,     -HOUR(5) },    /* USSR Zone 4 */
  424.     { "ist",    tZONE,     -(HOUR(5)+30) }, /* Indian Standard */
  425.     { "zp6",    tZONE,     -HOUR(6) },    /* USSR Zone 5 */
  426.     { "nst",    tZONE,     -HOUR(7) },    /* North Sumatra */
  427.     { "sst",    tZONE,     -HOUR(7) },    /* South Sumatra */
  428.     { "jt",    tZONE,     -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
  429.     { "nzt",    tZONE,     -HOUR(12) },    /* New Zealand */
  430.     { "idle",    tZONE,     -HOUR(12) },    /* International Date Line East */
  431.     { "cat",    tZONE,     HOUR(10) },    /* -- expired 1967 */
  432.     { "nt",    tZONE,     HOUR(11) },    /* -- expired 1967 */
  433.     { "ahst",    tZONE,     HOUR(10) },    /* -- expired 1983 */
  434.     { "hdt",    tDAYZONE,  HOUR(10) },    /* -- expired 1986 */
  435. #endif /* 0 */
  436. };
  437.  
  438.  
  439. /* ARGSUSED */
  440. static void
  441. date_error(s)
  442.     char    *s;
  443. {
  444.     /* NOTREACHED */
  445. }
  446.  
  447.  
  448. static time_t
  449. ToSeconds(Hours, Minutes, Seconds, Meridian)
  450.     time_t    Hours;
  451.     time_t    Minutes;
  452.     time_t    Seconds;
  453.     MERIDIAN    Meridian;
  454. {
  455.     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
  456.     return -1;
  457.     if (Meridian == MER24) {
  458.     if (Hours < 0 || Hours > 23)
  459.         return -1;
  460.     }
  461.     else {
  462.     if (Hours < 1 || Hours > 12)
  463.         return -1;
  464.     if (Hours == 12)
  465.         Hours = 0;
  466.     if (Meridian == MERpm)
  467.         Hours += 12;
  468.     }
  469.     return (Hours * 60L + Minutes) * 60L + Seconds;
  470. }
  471.  
  472.  
  473. static time_t
  474. Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, dst)
  475.     time_t    Month;
  476.     time_t    Day;
  477.     time_t    Year;
  478.     time_t    Hours;
  479.     time_t    Minutes;
  480.     time_t    Seconds;
  481.     MERIDIAN    Meridian;
  482.     DSTMODE    dst;
  483. {
  484.     static int    DaysNormal[13] = {
  485.     0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  486.     };
  487.     static int    DaysLeap[13] = {
  488.     0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  489.     };
  490.     static int    LeapYears[] = {
  491.     1972, 1976, 1980, 1984, 1988, 1992, 1996,
  492.     2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
  493.     };
  494.     register int    *yp;
  495.     register int    *mp;
  496.     register time_t    Julian;
  497.     register int    i;
  498.     time_t        tod;
  499.  
  500.     if (Year < 0)
  501.     Year = -Year;
  502.     if (Year < 100)
  503.     Year += 1900;
  504.     if (Year < EPOCH)
  505.     Year += 100;
  506.     for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++)
  507.     if (Year == *yp) {
  508.         mp = DaysLeap;
  509.         break;
  510.     }
  511.     if (Year < EPOCH || Year > END_OF_TIME
  512.      || Month < 1 || Month > 12
  513.      /* NOSTRICT *//* conversion from long may lose accuracy */
  514.      || Day < 1 || Day > mp[(int)Month])
  515.     return -1;
  516.  
  517.     Julian = Day - 1 + (Year - EPOCH) * 365;
  518.     for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++)
  519.     if (Year <= *yp)
  520.         break;
  521.     for (i = 1; i < Month; i++)
  522.     Julian += *++mp;
  523.     Julian *= SECSPERDAY;
  524.     Julian += yyTimezone * 60L;
  525.     if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
  526.     return -1;
  527.     Julian += tod;
  528.     tod = Julian;
  529.     if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
  530.     Julian -= DST_OFFSET * 60L * 60L;
  531.     return Julian;
  532. }
  533.  
  534.  
  535. static time_t
  536. DSTcorrect(Start, Future)
  537.     time_t    Start;
  538.     time_t    Future;
  539. {
  540.     time_t    StartDay;
  541.     time_t    FutureDay;
  542.  
  543.     StartDay = (localtime(&Start)->tm_hour + 1) % 24;
  544.     FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
  545.     return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60L * 60L;
  546. }
  547.  
  548.  
  549. static time_t
  550. RelativeMonth(Start, RelMonth)
  551.     time_t    Start;
  552.     time_t    RelMonth;
  553. {
  554.     struct tm    *tm;
  555.     time_t    Month;
  556.     time_t    Year;
  557.  
  558.     tm = localtime(&Start);
  559.     Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
  560.     Year = Month / 12;
  561.     Month = Month % 12 + 1;
  562.     return DSTcorrect(Start,
  563.         Convert(Month, (time_t)tm->tm_mday, Year,
  564.         (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
  565.         MER24, DSTmaybe));
  566. }
  567.  
  568.  
  569. static int
  570. LookupWord(buff, length)
  571.     char        *buff;
  572.     register int    length;
  573. {
  574.     register char    *p;
  575.     register char    *q;
  576.     register TABLE    *tp;
  577.     register int    c;
  578.  
  579.     p = buff;
  580.     c = p[0];
  581.  
  582.     /* See if we have an abbreviation for a month. */
  583.     if (length == 3 || (length == 4 && p[3] == '.'))
  584.     for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
  585.         q = tp->name;
  586.         if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
  587.         yylval.Number = tp->value;
  588.         return tp->type;
  589.         }
  590.     }
  591.     else
  592.     for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++)
  593.         if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
  594.         yylval.Number = tp->value;
  595.         return tp->type;
  596.         }
  597.  
  598.     /* Try for a timezone. */
  599.     for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
  600.     if (c == tp->name[0] && p[1] == tp->name[1]
  601.      && strcmp(p, tp->name) == 0) {
  602.         yylval.Number = tp->value;
  603.         return tp->type;
  604.     }
  605.  
  606.     /* Try the units table. */
  607.     for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
  608.     if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
  609.         yylval.Number = tp->value;
  610.         return tp->type;
  611.     }
  612.  
  613.     /* Strip off any plural and try the units table again. */
  614.     if (--length > 0 && p[length] == 's') {
  615.     p[length] = '\0';
  616.     for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
  617.         if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
  618.         p[length] = 's';
  619.         yylval.Number = tp->value;
  620.         return tp->type;
  621.         }
  622.     p[length] = 's';
  623.     }
  624.     length++;
  625.  
  626.     /* Drop out any periods. */
  627.     for (p = buff, q = (char*)buff; *q; q++)
  628.     if (*q != '.')
  629.         *p++ = *q;
  630.     *p = '\0';
  631.  
  632.     /* Try the meridians. */
  633.     if (buff[1] == 'm' && buff[2] == '\0') {
  634.     if (buff[0] == 'a') {
  635.         yylval.Meridian = MERam;
  636.         return tMERIDIAN;
  637.     }
  638.     if (buff[0] == 'p') {
  639.         yylval.Meridian = MERpm;
  640.         return tMERIDIAN;
  641.     }
  642.     }
  643.  
  644.     /* If we saw any periods, try the timezones again. */
  645.     if (p - buff != length) {
  646.     c = buff[0];
  647.     for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
  648.         if (c == tp->name[0] && p[1] == tp->name[1]
  649.         && strcmp(p, tp->name) == 0) {
  650.         yylval.Number = tp->value;
  651.         return tp->type;
  652.         }
  653.     }
  654.  
  655.     /* Unknown word -- assume GMT timezone. */
  656.     yylval.Number = 0;
  657.     return tZONE;
  658. }
  659.  
  660.  
  661. int
  662. date_lex()
  663. {
  664.     register char    c;
  665.     register char    *p;
  666.     char        buff[20];
  667.     register int    sign;
  668.     register int    i;
  669.     register int    nesting;
  670.  
  671.     for ( ; ; ) {
  672.     /* Get first character after the whitespace. */
  673.     for ( ; ; ) {
  674.         while (isspace(*yyInput))
  675.         yyInput++;
  676.         c = *yyInput;
  677.  
  678.         /* Ignore RFC 822 comments, typically time zone names. */
  679.         if (c != LPAREN)
  680.         break;
  681.         for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; )
  682.         if (c == LPAREN)
  683.             nesting++;
  684.         else if (!IS7BIT(c) || c == '\0' || c == '\r'
  685.              || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c))))
  686.             /* Lexical error: bad comment. */
  687.             return '?';
  688.         yyInput++;
  689.     }
  690.  
  691.     /* A number? */
  692.     if (isdigit(c) || c == '-' || c == '+') {
  693.         if (c == '-' || c == '+') {
  694.         sign = c == '-' ? -1 : 1;
  695.         yyInput++;
  696.         if (!isdigit(*yyInput))
  697.             /* Skip the plus or minus sign. */
  698.             continue;
  699.         }
  700.         else
  701.         sign = 0;
  702.         for (i = 0; (c = *yyInput++) != '\0' && isdigit(c); )
  703.         i = 10 * i + c - '0';
  704.         yyInput--;
  705.         yylval.Number = sign < 0 ? -i : i;
  706.         return sign ? tSNUMBER : tUNUMBER;
  707.     }
  708.  
  709.     /* A word? */
  710.     if (isalpha(c)) {
  711.         for (p = buff; (c = *yyInput++) == '.' || isalpha(c); )
  712.         if (p < &buff[sizeof buff - 1])
  713.             *p++ = isupper(c) ? tolower(c) : c;
  714.         *p = '\0';
  715.         yyInput--;
  716.         return LookupWord(buff, p - buff);
  717.     }
  718.  
  719.     return *yyInput++;
  720.     }
  721. }
  722.  
  723.  
  724. time_t
  725. parsedate(p)
  726.     char        *p;
  727. {
  728.     extern int        date_parse();
  729.     time_t        Start;
  730.  
  731.     yyInput = p;
  732.  
  733.     yyYear = 0;
  734.     yyMonth = 0;
  735.     yyDay = 0;
  736.     yyTimezone = 0;
  737.     yyDSTmode = DSTmaybe;
  738.     yyHour = 0;
  739.     yyMinutes = 0;
  740.     yySeconds = 0;
  741.     yyMeridian = MER24;
  742.     yyRelSeconds = 0;
  743.     yyRelMonth = 0;
  744.     yyHaveDate = 0;
  745.     yyHaveRel = 0;
  746.     yyHaveTime = 0;
  747.  
  748.     if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
  749.     return -1;
  750.  
  751.     if (yyHaveDate || yyHaveTime) {
  752.     Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
  753.             yyMeridian, yyDSTmode);
  754.     if (Start < 0)
  755.         return -1;
  756.     }
  757.     else
  758.     return -1;
  759.  
  760.     Start += yyRelSeconds;
  761.     if (yyRelMonth)
  762.     Start += RelativeMonth(Start, yyRelMonth);
  763.  
  764.     /* Have to do *something* with a legitimate -1 so it's distinguishable
  765.      * from the error return value.  (Alternately could set errno on error.) */
  766.     return Start == -1 ? 0 : Start;
  767. }
  768.  
  769.  
  770. #ifdef TEST
  771.  
  772. #if YYDEBUG
  773. extern int    yydebug;
  774. #endif /* YYDEBUG */
  775.  
  776. /* ARGSUSED */
  777. int
  778. main(ac, av)
  779.     int        ac;
  780.     char    *av[];
  781. {
  782.     char    buff[128];
  783.     time_t    d;
  784.  
  785. #if YYDEBUG
  786.     yydebug = 1;
  787. #endif /* YYDEBUG */
  788.  
  789.     (void)printf("Enter date, or blank line to exit.\n\t> ");
  790.     for ( ; ; ) {
  791.     (void)printf("\t> ");
  792.     (void)fflush(stdout);
  793.     if (gets(buff) == NULL || buff[0] == '\n')
  794.         break;
  795. #if YYDEBUG
  796.     if (strcmp(buff, "yydebug") == 0) {
  797.         yydebug = !yydebug;
  798.         printf("yydebug = %s\n", yydebug ? "on" : "off");
  799.         continue;
  800.     }
  801. #endif /* YYDEBUG */
  802.     d = parsedate(buff, (TIMEINFO *)NULL);
  803.     if (d == -1)
  804.         (void)printf("Bad format - couldn't convert.\n");
  805.     else
  806.         (void)printf("%s", ctime(&d));
  807.     }
  808.  
  809.     exit(0);
  810.     /* NOTREACHED */
  811. }
  812. #endif /* TEST */
  813.